package com.iteaj.iot.taos;

import com.iteaj.iot.tools.annotation.*;
import com.iteaj.iot.tools.db.DefaultFieldMeta;
import com.iteaj.iot.tools.db.FieldMeta;
import com.iteaj.iot.tools.db.TableFieldMapper;
import com.iteaj.iot.tools.db.sql.SqlAnnotationMeta;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.jdbc.core.SqlParameterValue;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

public class TaosSqlMeta extends SqlAnnotationMeta {

    /**
     * 是否使用超级表自动创建数据表
     */
    boolean using;

    private STable sTable;

    /**
     * 数据表表名 支持使用SpEL表达式
     * @see STable#table()
     */
    private String dataTableName;

    /**
     * 超级表表名
     */
    private String sTableName;

    private String insertSql;

    private String paramSql;

    private String tagInsertSql;

    private String tagParamSql;

    private TagsResolver tagsResolver;

    private List<FieldMeta> tags = new ArrayList<>();

    private static SqlParameterValue NULL = new SqlParameterValue(Types.NULL, null);

    /**
     * 使用注解生成元数据
     * @see STable
     * @see #build(BeanFactory)
     * @param entityClass
     */
    protected TaosSqlMeta(Class<?> entityClass) {
        super(entityClass);
    }

    /**
     * 自定义元数据 自动创建数据表
     * @param stableName 超级表表名
     * @param dataTableName 支持使用SpEL表达式
     * @param fieldMetas
     */
    public TaosSqlMeta(String stableName, String dataTableName, List<FieldMeta> fieldMetas) {
        this(stableName, dataTableName, fieldMetas, true);
    }

    /**
     * 自定义元数据 自动创建数据表
     * @param using 是否自动创建数据表
     * @param stableName 超级表表名
     * @param dataTableName 支持使用SpEL表达式
     * @param fieldMetas
     */
    public TaosSqlMeta(String stableName, String dataTableName, List<FieldMeta> fieldMetas, boolean using) {
        this(stableName, dataTableName, fieldMetas, null, null);
        this.using = using;
    }

    /**
     * 自定义元数据 自动创建数据表
     * @param stableName 超级表表名
     * @param dataTableName 支持使用SpEL表达式
     * @param fieldMetas
     * @param tagMetas
     * @param resolver
     */
    public TaosSqlMeta(String stableName, String dataTableName, List<FieldMeta> fieldMetas, List<FieldMeta> tagMetas, TagsResolver resolver) {
        super(stableName, fieldMetas);
        this.using = true;
        this.tags = tagMetas;
        this.tagsResolver = resolver;
        this.sTableName = stableName;
        this.dataTableName = dataTableName;
        this.handleSqlStatement();
    }

    @Override
    protected void resolveEntityTypeToMetas(Class<?> entityClass) { }

    @Override
    protected void resolveInsertSql() { }

    protected TaosSqlMeta build(BeanFactory factory) {
        this.sTable = getEntityClass().getAnnotation(STable.class);
        if(this.sTable == null) return null;

        this.using = this.sTable.using();
        this.sTableName = this.sTable.value();
        this.dataTableName = this.sTable.table();

        if(!StringUtils.hasText(this.dataTableName)) {
            throw new TaosException("数据表名必填[STable#table()]");
        }

        Field[] declaredFields = getEntityClass().getDeclaredFields();
        for(Field item : declaredFields) {
            if(!item.isAccessible()) {
                item.setAccessible(true);
            }

            IotTableId fieldId = item.getAnnotation(IotTableId.class);
            if(fieldId != null) {
                if(!Date.class.isAssignableFrom(item.getType()) && Long.class != item.getType() && long.class != item.getType()) {
                    throw new TaosException("Id注解字段["+item.getName()+"]必须是类型[Date or Long]");
                }

                this.getFieldMetas().add(0, new IotTableIdMeta(fieldId, item));
            }

            IotField field = item.getAnnotation(IotField.class);
            if(field != null) {
                this.getFieldMetas().add(new IotFieldMeta(field, item));
            }
            //todo 暂时不支持使用注解的形式处理tags
//            IotTag iotTag = item.getAnnotation(IotTag.class);
//            if(iotTag != null) {
//                tags.add(new IotTagMeta(iotTag, item));
//            }
        }

        if(!ObjectUtils.isEmpty(this.sTable.tags())) {
            if(StringUtils.hasText(this.sTable.tagsResolver())) {
                this.tagsResolver = factory.getBean(this.sTable.tagsResolver(), TagsResolver.class);
            } else {
                throw new TaosException("未设置STable#tagsResolver()");
            }

            this.tags = Arrays.stream(this.sTable.tags())
                    .map(item -> new DefaultFieldMeta(158168, item))
                    .collect(Collectors.toList());
        }

        return this.handleSqlStatement();
    }

    protected TaosSqlMeta handleSqlStatement() {
        this.paramSql = "("+this.getFieldMetas().stream().map(item -> "?").collect(Collectors.joining(","))+")";
        this.insertSql = "(" + this.getFieldMetas().stream().map(item -> item.getName()).collect(Collectors.joining(","))+")";

        if(this.isUsing()) {
            if(!StringUtils.hasText(this.sTableName)) {
                throw new TaosException("使用超级表自动创建数据表时超级表的表名必填");
            }

            if(!CollectionUtils.isEmpty(this.tags)) {
                this.tagInsertSql = "USING " + sTable.value() + " (" + tags.stream().map(item -> item.getName()).collect(Collectors.joining(",")) + ")";
                this.tagParamSql = "(" + this.tags.stream().map(item -> "?").collect(Collectors.joining(",")) + ")";
            }
        }

        return this;
    }

    /**
     * 超级表表名
     * @return
     */
    @Override
    public String getTableName() {
        return this.sTableName;
    }

    public List<SqlParameterValue> getTagParams(String tableName) {
        List<SqlParameterValue> params = new ArrayList<>();
        this.tags.forEach(item -> {
            Object tagValue = this.tagsResolver.resolve(tableName, item.getName());
            if(tagValue != null) {
                params.add(new SqlParameterValue(TableFieldMapper.valueToTableType(tagValue), tagValue));
            } else {
                params.add(NULL);
            }
        });

        return params;
    }

    public String getInsertSql() {
        return insertSql;
    }

    public String getParamSql() {
        return paramSql;
    }

    public STable getsTable() {
        return sTable;
    }

    public TagsResolver getTagsResolver() {
        return tagsResolver;
    }

    public List<FieldMeta> getTags() {
        return tags;
    }

    public String getTagInsertSql() {
        return tagInsertSql;
    }

    public void setTagInsertSql(String tagInsertSql) {
        this.tagInsertSql = tagInsertSql;
    }

    public String getTagParamSql() {
        return tagParamSql;
    }

    public void setTagParamSql(String tagParamSql) {
        this.tagParamSql = tagParamSql;
    }

    public String getDataTableName() {
        return dataTableName;
    }

    public boolean isUsing() {
        return using;
    }
}
